<!--
/*
 * Copyright 2017 Google Inc. All Rights Reserved.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
-->
<!DOCTYPE html>
<html lang="en">
<head>
  <title>three.ar.js - Load Model</title>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, user-scalable=no,
  minimum-scale=1.0, maximum-scale=1.0">
  <style>
    body {
      font-family: monospace;
      margin: 0;
      overflow: hidden;
      position: fixed;
      width: 100%;
      height: 100vh;
      -webkit-user-select: none;
      user-select: none;
    }
    #info {
      position: absolute;
      left: 50%;
      bottom: 0;
      transform: translate(-50%, 0);
      margin: 1em;
      z-index: 10;
      display: block;
      width: 100%;
      line-height: 2em;
      text-align: center;
    }
    #info * {
      color: #fff;
    }
    .title {
      background-color: rgba(40, 40, 40, 0.4);
      padding: 0.4em 0.6em;
      border-radius: 0.1em;
    }
    .links {
      background-color: rgba(40, 40, 40, 0.6);
      padding: 0.4em 0.6em;
      border-radius: 0.1em;
    }
    canvas {
      position: absolute;
      top: 0;
      left: 0;
    }
  </style>
</head>
<body>
<div id="info">
  <span class="title">Tap to spawn an object from <a href="https://poly.google.com">Poly</a>.</span><br/>
  <span class="title">
    <a href="https://poly.google.com/view/dK08uQ8-Zm9">Model</a> by
    <a href="https://poly.google.com/user/f8cGQY15_-g">Naomi Chen</a> /
    <a href="https://creativecommons.org/licenses/by/2.0/">CC-BY</a>
  </span><br />
  <span class="links">
    <a href="https://github.com/google-ar/three.ar.js">three.ar.js</a> -
    <a href="https://developers.google.com/ar/develop/web/getting-started#examples">examples</a>
  </span>
</div>
<script src="../third_party/three.js/three.js"></script>
<script src="../third_party/three.js/VRControls.js"></script>
<script src="../third_party/three.js/OBJLoader.js"></script>
<script src="../third_party/three.js/MTLLoader.js"></script>
<script src="../dist/three.ar.js"></script>
<script>

var vrDisplay;
var vrControls;
var arView;

var canvas;
var camera;
var scene;
var renderer;
var model;

var shadowMesh;
var planeGeometry;
var light;
var directionalLight;

var OBJ_PATH = './assets/ArcticFox_Posed.obj';
var MTL_PATH = './assets/ArcticFox_Posed.mtl';
var SCALE = 0.1;

/**
 * Use the `getARDisplay()` utility to leverage the WebVR API
 * to see if there are any AR-capable WebVR VRDisplays. Returns
 * a valid display if found. Otherwise, display the unsupported
 * browser message.
 */
THREE.ARUtils.getARDisplay().then(function (display) {
  if (display) {
    vrDisplay = display;
    init();
  } else {
    THREE.ARUtils.displayUnsupportedMessage();
  }
});

function init() {
  // Turn on the debugging panel
  var arDebug = new THREE.ARDebug(vrDisplay);
  document.body.appendChild(arDebug.getElement());

  // Setup the three.js rendering environment
  renderer = new THREE.WebGLRenderer({ alpha: true });
  renderer.setPixelRatio(window.devicePixelRatio);
  renderer.setSize(window.innerWidth, window.innerHeight);
  renderer.autoClear = false;
  canvas = renderer.domElement;
  document.body.appendChild(canvas);
  scene = new THREE.Scene();

  // Creating the ARView, which is the object that handles
  // the rendering of the camera stream behind the three.js
  // scene
  arView = new THREE.ARView(vrDisplay, renderer);

  // The ARPerspectiveCamera is very similar to THREE.PerspectiveCamera,
  // except when using an AR-capable browser, the camera uses
  // the projection matrix provided from the device, so that the
  // perspective camera's depth planes and field of view matches
  // the physical camera on the device.
  camera = new THREE.ARPerspectiveCamera(
    vrDisplay,
    60,
    window.innerWidth / window.innerHeight,
    vrDisplay.depthNear,
    vrDisplay.depthFar
  );

  // VRControls is a utility from three.js that applies the device's
  // orientation/position to the perspective camera, keeping our
  // real world and virtual world in sync.
  vrControls = new THREE.VRControls(camera);

  // For shadows to work
  renderer.shadowMap.enabled = true;
  renderer.shadowMap.type = THREE.PCFSoftShadowMap;

  // The materials in Poly models will render as a black mesh
  // without lights in our scenes. Let's add an ambient light
  // so our model can be scene, as well as a directional light
  // for the shadow
  directionalLight = new THREE.DirectionalLight();
  // @TODO in the future, use AR light estimation
  directionalLight.intensity = 0.3;
  directionalLight.position.set(10, 15, 10);
  // We want this light to cast shadow
  directionalLight.castShadow = true;
  light = new THREE.AmbientLight();
  scene.add(light);
  scene.add(directionalLight);

  // Make a large plane to receive our shadows
  planeGeometry = new THREE.PlaneGeometry(2000, 2000);
  // Rotate our plane to be parallel to the floor
  planeGeometry.rotateX(-Math.PI / 2);

  // Create a mesh with a shadow material, resulting in a mesh
  // that only renders shadows once we flip the `receiveShadow` property
  shadowMesh = new THREE.Mesh(planeGeometry, new THREE.ShadowMaterial({
    color: 0x111111,
    opacity: 0.15,
  }));
  shadowMesh.receiveShadow = true;
  scene.add(shadowMesh);

  THREE.ARUtils.loadModel({
    objPath: OBJ_PATH,
    mtlPath: MTL_PATH,
    OBJLoader: undefined, // uses window.THREE.OBJLoader by default
    MTLLoader: undefined, // uses window.THREE.MTLLoader by default
  }).then(function(group) {
    model = group;
    // As OBJ models may contain a group with several meshes,
    // we want all of them to cast shadow
    model.children.forEach(function(mesh) { mesh.castShadow = true; });

    model.scale.set(SCALE, SCALE, SCALE);

    // Place the model very far to initialize
    model.position.set(10000, 10000, 10000);
    scene.add(model);
  });

  // Bind our event handlers
  window.addEventListener('resize', onWindowResize, false);
  canvas.addEventListener('click', onClick, false);

  // Kick off the render loop!
  update();
}

/**
 * The render loop, called once per frame. Handles updating
 * our scene and rendering.
 */
function update() {
  // Clears color from the frame before rendering the camera (arView) or scene.
  renderer.clearColor();

  // Render the device's camera stream on screen first of all.
  // It allows to get the right pose synchronized with the right frame.
  arView.render();

  // Update our camera projection matrix in the event that
  // the near or far planes have updated
  camera.updateProjectionMatrix();

  // Update our perspective camera's positioning
  vrControls.update();

  // Render our three.js virtual scene
  renderer.clearDepth();
  renderer.render(scene, camera);

  // Kick off the requestAnimationFrame to call this function
  // when a new VRDisplay frame is rendered
  vrDisplay.requestAnimationFrame(update);
}

/**
 * On window resize, update the perspective camera's aspect ratio,
 * and call `updateProjectionMatrix` so that we can get the latest
 * projection matrix provided from the device
 */
function onWindowResize () {
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();
  renderer.setSize(window.innerWidth, window.innerHeight);
}

/**
 * When clicking on the screen, fire a ray from where the user clicked
 * on the screen and if a hit is found, place a cube there.
 */
function onClick (e) {
  // Inspect the event object and generate normalize screen coordinates
  // (between 0 and 1) for the screen position.
  var x = e.clientX / window.innerWidth;
  var y = e.clientY / window.innerHeight;

  // Send a ray from the point of click to the real world surface
  // and attempt to find a hit. `hitTest` returns an array of potential
  // hits.
  var hits = vrDisplay.hitTest(x, y);

  if (!model) {
    console.warn('Model not yet loaded');
    return;
  }

  // If a hit is found, just use the first one
  if (hits && hits.length) {
    var hit = hits[0];

    // Turn the model matrix from the VRHit into a
    // THREE.Matrix4 so we can extract the position
    // elements out so we can position the shadow mesh
    // to be directly under our model. This is a complicated
    // way to go about it to illustrate the process, and could
    // be done by manually extracting the "Y" value from the
    // hit matrix via `hit.modelMatrix[13]`
    var matrix = new THREE.Matrix4();
    var position = new THREE.Vector3();
    matrix.fromArray(hit.modelMatrix);
    position.setFromMatrixPosition(matrix);

    // Set our shadow mesh to be at the same Y value
    // as our hit where we're placing our model
    // @TODO use the rotation from hit.modelMatrix
    shadowMesh.position.y = position.y;

    // Use the `placeObjectAtHit` utility to position
    // the cube where the hit occurred
    THREE.ARUtils.placeObjectAtHit(model,  // The object to place
                                   hit,   // The VRHit object to move the cube to
                                   1,     // Easing value from 0 to 1; we want to move
                                          // the cube directly to the hit position
                                   true); // Whether or not we also apply orientation

    // Rotate the model to be facing the user
    var angle = Math.atan2(
      camera.position.x - model.position.x,
      camera.position.z - model.position.z
    );
    model.rotation.set(0, angle, 0);
  }
}
</script>
</body>
</html>
